;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                                 ;;
;;  STACK.INC                                                      ;;
;;                                                                 ;;
;;  TCP/IP stack for Menuet OS                                     ;;
;;                                                                 ;;
;;  Version 0.7  4th July 2004                                     ;;
;;                                                                 ;;
;;  Copyright 2002 Mike Hibbett, mikeh@oceanfree.net               ;;
;;                                                                 ;;
;;  See file COPYING for details                                   ;;
;;                                                                 ;;
;; Version 0.7                                                     ;;
;;         Added a timer per socket to allow delays when rx window ;;
;;         gets below 1KB                                          ;;
;;                                                                 ;;
;;  10.01.2007 Bugfix for checksum function from Paolo Franchetti  ;;
;;                                                                 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   
   
;*******************************************************************
;   Interface
;      The interfaces defined in ETHERNET.INC plus:
;      stack_init
;      stack_handler
;      app_stack_handler
;      app_socket_handler
;      checksum
;
;*******************************************************************
   
   
   
;
;   IP Packet after reception - Normal IP packet format
;
;    0                   1                   2                   3
;    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
;
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;0  |Version|  IHL  |Type of Service|       Total Length            |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;4  |         Identification        |Flags|      Fragment Offset    |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;8  |  Time to Live |    Protocol   |         Header Checksum       |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;12 |                       Source Address                          |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;16 |                    Destination Address                        |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |      Data                                                     |
;   +-+-+-..........                                               -+
   
  
;   TCP Payload ( Data field in IP datagram )
;
;    0                   1                   2                   3   
;    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;20 |          Source Port          |       Destination Port        |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;24 |                        Sequence Number                        |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;28 |                    Acknowledgment Number                      |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;32 |  Data |           |U|A|P|R|S|F|                               |
;   | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
;   |       |           |G|K|H|T|N|N|                               |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;36 |           Checksum            |         Urgent Pointer        |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;40 |                    Options                    |    Padding    |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |                             data                              
   
   
;
;   UDP Payload ( Data field in IP datagram )
;
;    0                   1                   2                   3
;    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
;
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |       Source Port             |      Destination Port         |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   | Length ( UDP Header + Data )  |           Checksum            |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |       UDP Data                                                |
;   +-+-+-..........                                               -+
;

   
;
;  Socket Descriptor + Buffer
;
;    0                   1                   2                   3
;    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
;
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |                    Status ( of this buffer )                  |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |  Application Process ID                                       |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |                  Local IP Address                             |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   | Local IP Port                 | Unused ( set to 0 )           |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   |                  Remote IP Address                            |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;   | Remote IP Port                | Unused ( set to 0 )           |
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 24|   Rx Data Count                                   INTEL format|
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 28|                 TCB STATE                         INTEL format|
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 32|   TCB Timer (seconds)                             INTEL format|
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 36| ISS (Inital Sequence # used by this connection )   INET format|
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 40| IRS ( Inital Receive Sequence # )                  INET format|
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 44| SND.UNA  Seq # of unack'ed sent packets            INET format|
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 48| SND.NXT  Next send seq # to use                    INET format|
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 52| SND.WND  Send window                               INET format|
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 56| RCV.NXT  Next expected receive sequence #          INET format|
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 60| RCV.WND  Receive window                            INET format|
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 64| SEG.LEN  Segment length                           INTEL format|
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 68| SEG.WND  Segment window                           INTEL format|
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 72| Retransmit queue # NOW WINDOW SIZE TIMER          INTEL format|
;   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 76|       RX Data                                                 |
;   +-+-+-..........                                               -+
   
   
   
; IP protocol numbers
PROTOCOL_ICMP     equ      1
PROTOCOL_TCP      equ      6
PROTOCOL_UDP      equ      17
   
   
; TIPBUFF status values
BUFF_EMPTY         equ     0
BUFF_RX_FULL       equ     1
BUFF_ALLOCATED     equ     2
BUFF_TX_FULL       equ     3
   
NUM_IPBUFFERS      equ     20    ; buffers allocated for TX/RX
   
SOCK_EMPTY         equ     0        ; socket not in use
SOCK_OPEN          equ     1        ; open issued, but no data sent

; TCP opening modes
SOCKET_PASSIVE     equ     0
SOCKET_ACTIVE      equ     1
   
; TCP TCB states
TCB_LISTEN         equ        1
TCB_SYN_SENT       equ        2
TCB_SYN_RECEIVED   equ        3
TCB_ESTABLISHED    equ        4
TCB_FIN_WAIT_1     equ        5
TCB_FIN_WAIT_2     equ        6
TCB_CLOSE_WAIT     equ        7
TCB_CLOSING        equ        8
TCB_LAST_ACK       equ        9
TCB_TIME_WAIT      equ        10
TCB_CLOSED         equ        11
    
TWOMSL              equ     10      ; # of secs to wait before closing socket
   
; socket buffers
SOCKETBUFFSIZE     equ        4096  ; state + config + buffer.
SOCKETHEADERSIZE   equ        76    ; thus 4096 - SOCKETHEADERSIZE bytes data
                               
NUM_SOCKETS        equ        16    ; Number of open sockets supported. Was 20
   

NUMQUEUES          equ        4
EMPTY_QUEUE        equ        0
IPIN_QUEUE         equ        1
IPOUT_QUEUE        equ        2
NET1OUT_QUEUE      equ        3

NO_BUFFER          equ        0xFFFF            
IPBUFFSIZE         equ        1500                ; MTU of an ethernet packet
NUMQUEUEENTRIES    equ        NUM_IPBUFFERS
NUMRESENDENTRIES    equ         18              ; Buffers for TCP resend packets
TCP_RETRIES         equ         5               ; Number of times to resend a packet
TCP_TIMEOUT         equ         10              ; resend if not replied to in x hs
   
; These are the 0x40 function codes for application access to the stack
STACK_DRIVER_STATUS  equ   52
SOCKET_INTERFACE     equ   53

   
; 128KB allocated for the stack and network driver buffers and other
; data requirements
stack_data_start     equ   0x700000
eth_data_start       equ   0x700000
stack_data           equ   0x704000
stack_data_end       equ   0x71ffff
   
; 32 bit word   
stack_config         equ   stack_data      
; 32 bit word - IP Address in network format
stack_ip             equ   stack_data + 4                    
; 1 byte. 0 == inactive, 1 = active
slip_active          equ   stack_data + 8       ; no longer used
; 1 byte. 0 == inactive, 1 = active
ethernet_active      equ   stack_data + 9
unused               equ   stack_data + 10
;  word. Buffer number, -1 if none
rx_buff_ptr          equ   stack_data + 12
; dword. Buffer number, -1 if none
tx_buff_ptr          equ   stack_data + 16
; byte.
slip_rx_state        equ   stack_data + 20      ; no longer used
; byte
slip_tx_state        equ   stack_data + 21      ; no longer used
; dword. Index into data
rx_data_ptr          equ   stack_data + 22
; dword. Index into data
tx_data_ptr          equ   stack_data + 26
; word. Count of bytes to send
tx_msg_len           equ   stack_data + 30
; Address of selected socket
sktAddr              equ   stack_data + 32
; Parameter to checksum routine - data ptr
checkAdd1            equ   stack_data + 36
; Parameter to checksum routine - 2nd data ptr
checkAdd2            equ   stack_data + 40
; Parameter to checksum routine - data size
checkSize1           equ   stack_data + 44
; Parameter to checksum routine - 2nd data size
checkSize2           equ   stack_data + 46
; result of checksum routine
checkResult          equ   stack_data + 48
   
; holds the TCP/UDP pseudo header. SA|DA|0|prot|UDP len|
pseudoHeader         equ   stack_data + 50
   
; receive and transmit IP buffer allocation
sockets              equ   stack_data + 62
Next_free2           equ   sockets + (SOCKETBUFFSIZE * NUM_SOCKETS)
; 1560 byte buffer for rx / tx ethernet packets
Ether_buffer         equ   Next_free2
Next_free3           equ   Ether_buffer + 1560
last_1sTick          equ   Next_free3
IPbuffs              equ   Next_free3 + 1
queues               equ   IPbuffs + ( NUM_IPBUFFERS * IPBUFFSIZE )
queueList            equ   queues + (2 * NUMQUEUES)
last_1hsTick         equ   queueList + ( 2 * NUMQUEUEENTRIES )

;resendQ              equ   queueList + ( 2 * NUMQUEUEENTRIES )
;resendBuffer         equ    resendQ + ( 4 * NUMRESENDENTRIES ) ; for TCP
;                    equ    resendBuffer + ( IPBUFFSIZE * NUMRESENDENTRIES )



resendQ             equ     0x770000
resendBuffer        equ     resendQ + ( 4 * NUMRESENDENTRIES ) ; for TCP

  
;***************************************************************************
;   Function
;      stack_init
;
;   Description
;      Clear all allocated memory to zero. This ensures that
;       on startup, the stack is inactive, and consumes no resources
;       This is a kernel function, called prior to the OS main loop
;       in set_variables
;
;***************************************************************************
stack_init:
    xor     eax,eax
    mov     edi,stack_data_start
    mov     ecx,0x20000 / 4  ; Assume that we have 128KB of data
    cld
    rep     stosd
   
    ; Initialise TCP resend queue data structures
    mov     eax, 0xFFFFFFFF
    mov     edi, resendQ
    mov     ecx, NUMRESENDENTRIES  ; 1 dword per entry
    cld
    rep     stosd
    
   
    mov     eax, 0xFFFFFFFF
    mov     [rx_buff_ptr], eax
    mov     [tx_buff_ptr], eax
   
    ; Put in some defaults : slip, 0x3f8, 4, ip=192.168.1.22
    ; Saves me entering them each boot up when debugging
    mov     eax, 0x03f80401
    mov     [stack_config], eax
    mov     eax, 0xc801a8c0
    mov     [stack_ip], eax
   
    call    queueInit
   
    ; The following block sets up the 1s timer 
    mov     al,0x0   
    out     0x70,al
    in      al,0x71
    mov     [last_1sTick], al
      
    ret
   
   
   
;***************************************************************************
;   Function
;      stack_handler
;
;   Description
;       The kernel loop routine for the stack
;       This is a kernel function, called in the main loop
;
;***************************************************************************
stack_handler:

    call    ethernet_driver
    call    ip_rx
   
   
    ; Test for 10ms tick, call tcp timer 
    mov     eax,[0xfdf0]
    cmp     eax, [last_1hsTick]
    je      sh_001 
    
    mov     [last_1hsTick], eax
    call    tcp_tx_handler

sh_001:    
    
    ; Test for 1 second event, call 1s timer functions
    mov     al,0x0   ;second
    out     0x70,al
    in      al,0x71
    cmp     al, [last_1sTick]
    je      sh_exit
       
    mov     [last_1sTick], al
   
    call    arp_timer
    call    tcp_tcb_handler
   
sh_exit:   
    ret
   



;***************************************************************************
;   Function
;      is_localport_unused
;
;   Description
;         scans through all the active sockets , looking to see if the 
;      port number specified in bx is in use as a localport number.
;      This is useful when you want a to generate a unique local port
;      number.
;          On return, eax = 1 for free, 0 for in use 
;
;***************************************************************************
is_localport_unused:
    mov     al, bh
    mov     ah, bl
    mov     bx, ax
   
    mov     edx, SOCKETBUFFSIZE * NUM_SOCKETS
    mov     ecx, NUM_SOCKETS
    mov     eax, 0                    ; Assume the return value is 'in use'
    
ilu1:    
    sub     edx, SOCKETBUFFSIZE
    cmp     [edx + sockets + 12], bx
    loopnz  ilu1                  ; Return back if the socket is occupied
    
    jz      ilu_exit
    inc     eax                         ; return port not in use
    
ilu_exit:
    ret
   
   

;***************************************************************************
;   Function
;      get_free_socket
;
;   Description
;
;***************************************************************************
get_free_socket:
    push    ecx
    mov     eax, SOCKETBUFFSIZE * NUM_SOCKETS
    mov     ecx, NUM_SOCKETS
   
gfs1:
    sub     eax, SOCKETBUFFSIZE
    cmp     [eax + sockets], dword SOCK_EMPTY
    loopnz  gfs1                  ; Return back if the socket is occupied
    mov     eax, ecx
    pop     ecx
    jz      gfs_exit
    mov     eax, 0xFFFFFFFF
   
gfs_exit:
    ret
   
   
   
;***************************************************************************
;   Function
;      checksum
;
;   Description
;       checkAdd1,checkAdd2, checkSize1, checkSize2, checkResult
;       Dont break anything; Most registers are used by the caller
;       This code is derived from the 'C' source, cksum.c, in the book
;       Internetworking with TCP/IP Volume II by D.E. Comer
;
;***************************************************************************

checksum:

    pusha

    mov     eax, [checkAdd1]

    xor     edx, edx                  ; edx is the accumulative checksum
    xor     ebx, ebx
    mov     cx, [checkSize1]
    shr     cx, 1
    jz      cs1_1
   
cs1:

    mov     bh, [eax]
    mov     bl, [eax + 1]
   
    add     eax, 2
    add     edx, ebx
   
    loopw   cs1
   
cs1_1:

    and     word [checkSize1], 0x01
    jz      cs_test2

    mov     bh, [eax]
    xor     bl, bl
   
    add     edx, ebx
   
cs_test2:

    mov     cx, [checkSize2]
    cmp     cx, 0
    jz      cs_exit                     ; Finished if no 2nd buffer

    mov     eax, [checkAdd2]

    shr     cx, 1
    jz      cs2_1
       
cs2:
    mov     bh, [eax]
    mov     bl, [eax + 1]
   
    add     eax, 2
    add     edx, ebx
   
    loopw   cs2
   
cs2_1:
    and     word [checkSize2], 0x01
    jz      cs_exit

    mov     bh, [eax]
    xor     bl, bl   
   
    add     edx, ebx
   
cs_exit:
    mov     ebx, edx
   
    shr     ebx, 16
    and     edx, 0xffff
    add     edx, ebx
    mov     eax, edx
    shr     eax, 16
    add     edx, eax
    not     dx   
   
    mov     [checkResult], dx
    popa
    ret
   
   
   
   
;***************************************************************************
;   Function
;      app_stack_handler
;
;   Description
;       This is an application service, called by int 0x40 fn 52
;       It provides application access to the network interface layer
;
;***************************************************************************
app_stack_handler:
    cmp     eax, 0
    jnz     not0
    ; Read the configuartion word
    mov     eax, [stack_config]
    ret
   
not0:
    cmp     eax, 1
    jnz     not1
    ; read the IP address
   
    mov     eax, [stack_ip]
    ret
   
not1:
    cmp     eax, 2
    jnz     not2
   
    ; write the configuration word
    mov     [stack_config], ebx
   
    ; <Slip shouldn't be active anyway - thats an operational issue.>
    ; If ethernet now enabled, probe for the card, reset it and empty
    ; the packet buffer
    ; If all successfull, enable the card.
    ; If ethernet now disabled, set it as disabled. Should really
    ; empty the tcpip data area too.
   
    ; ethernet interface is '3' in ls 7 bits
    and     bl, 0x7f
    cmp     bl, 3
   
    je       ash_eth_enable
    ; Ethernet isn't enabled, so make sure that the card is disabled
    mov     [ethernet_active], byte 0
    
    ret
   
ash_eth_enable:
    ; Probe for the card. This will reset it and enable the interface
    ; if found
    call    eth_probe
    cmp     eax, 0
    je      ash_eth_done            ; Abort if no hardware found
   
    mov     [ethernet_active], byte 1
   
ash_eth_done:   
    ret
   
not2:
    cmp     eax, 3
    jnz     not3
    ; write the IP Address
    mov     [stack_ip], ebx
    ret
   
not3:
    cmp     eax, 4
    jnz     not4
    ; Enabled the slip driver on the comm port
    ; slip removed
    ret
   
not4:
    cmp     eax, 5
    jnz     not5
    ; Disable the slip driver on the comm port
    ; slip removed
    
not5:
    cmp     eax, 6
    jnz     not6
    
    ; Insert an IP packet into the stacks received packet queue
    call    stack_insert_packet
    ret
    
not6:
    cmp     eax, 7
    jnz     not7
    
    ; Test for any packets queued for transmission over the network
    
not7:
    cmp     eax, 8
    jnz     not8
    
    call    stack_get_packet
    ; Extract a packet queued for transmission by the network
    ret
    
not8:
    cmp     eax, 9
    jnz     not9
    
    ; read the gateway IP address
   
    mov     eax, [gateway_ip]
    ret

not9:
    cmp     eax, 10
    jnz     not10
    
    ; read the subnet mask
   
    mov     eax, [subnet_mask]
    ret

not10:
    cmp     eax, 11
    jnz     not11
    
    ; write the gateway IP Address
    mov     [gateway_ip], ebx

    ret

not11:
    cmp     eax, 12
    jnz     not12

    ; write the subnet mask
    mov     [subnet_mask], ebx
        

not12:
    cmp     eax, 13
    jnz     not13
    
    ; read the dns
   
    mov     eax, [dns_ip]
    ret

not13:
    cmp     eax, 14
    jnz     stack_driver_end
    
    ; write the dns IP Address
    mov     [dns_ip], ebx

    ret
    
stack_driver_end:
    ret
   
   
   
;***************************************************************************
;   Function
;      app_socket_handler
;
;   Description
;       This is an application service, called by int 0x40
;       It provides application access to stack socket services
;       such as opening sockets
;
;***************************************************************************
app_socket_handler:
    cmp     eax, 0
    jnz     nots0
   
    call    socket_open
    ret
   
nots0:
    cmp     eax, 1
    jnz     nots1
   
    call    socket_close
    ret
   
nots1:
    cmp     eax, 2
    jnz     nots2
   
    call    socket_poll
    ret
   
nots2:
    cmp     eax, 3
    jnz     nots3
   
    call    socket_read
    ret
   
nots3:
    cmp     eax, 4
    jnz     nots4
   
    call    socket_write
    ret
   
nots4:
    cmp     eax, 5
    jnz     nots5

    call    socket_open_tcp
    ret

nots5:
    cmp     eax, 6
    jnz     nots6
   
    call    socket_status
    ret

nots6:
    cmp     eax, 7
    jnz     nots7
   
    call    socket_write_tcp
    ret

nots7:
    cmp     eax, 8
    jnz     nots8
    
    call    socket_close_tcp
    ret
    
nots8:
    cmp     eax, 9
    jnz     nots9
    
    call    is_localport_unused
    ret
    
nots9:
    cmp     eax, 254
    jnz     notdump
   
    ret
   
notdump:
    cmp     eax, 255
    jnz     notsdebug
   
    ; This sub function allows access to debugging information on the stack
    ; ebx holds the request:
    ;  100 : return length of empty queue
    ;  101 : return length of IPOUT QUEUE
    ;  102 : return length of IPIN QUEUE
    ;  103 : return length of NET1OUT QUEUE
    ; 200 : return # of ARP entries
    ; 201 : return size of ARP table ( max # entries )
    ; 202 : select ARP table entry #
    ; 203 : return IP of selected table entry
    ; 204 : return High 4 bytes of MAC address of selected table entry
    ; 205 : return low  2 bytes of MAC address of selected table entry
    ; 206 : return status word of selected table entry
    ; 207 : return Time to live of selected table entry
    

    ;  2 : return number of IP packets received
    ;  3 : return number of packets transmitted
    ;  4 : return number of received packets dumped
    ;  5 : return number of arp packets received
    ;  6 : return status of packet driver
    ;      ( 0 == not active, FFFFFFFF = successful )
   
    call    stack_internal_status
    ret
   
notsdebug:
    ; Invalid Option
    ret
   

ARPTmp:    
times 14 db 0
   
;***************************************************************************
;   Function
;      stack_internal_status
;
;   Description
;       Returns information about the internal status of the stack
;       This is only useful for debugging
;       It works with the ethernet driver
;       sub function in ebx
;       return requested data in eax
;
;***************************************************************************
stack_internal_status:
    cmp     ebx, 100
    jnz     notsis100
   
    ;  100 : return length of EMPTY QUEUE
    mov     ebx, EMPTY_QUEUE
    call    queueSize   
    ret
   
notsis100:
    cmp     ebx, 101
    jnz     notsis101
   
    ;  101 : return length of IPOUT QUEUE
    mov     ebx, IPOUT_QUEUE
    call    queueSize   
    ret
   
notsis101:
    cmp     ebx, 102
    jnz     notsis102
   
    ;  102 : return length of IPIN QUEUE
    mov     ebx, IPIN_QUEUE
    call    queueSize   
    ret

notsis102:
    cmp     ebx, 103
    jnz     notsis103
   
    ;  103 : return length of NET1OUT QUEUE
    mov     ebx, NET1OUT_QUEUE
    call    queueSize   
    ret

notsis103:
    cmp     ebx, 200
    jnz     notsis200
    
    ; 200 : return num entries in arp table
    movzx   eax, byte [NumARP]
    ret
    
notsis200:
    cmp     ebx, 201
    jnz     notsis201
    
    ; 201 : return arp table size
    mov     eax, 20 ; ARP_TABLE_SIZE
    ret

notsis201:
    cmp     ebx, 202
    jnz     notsis202

    ; 202 - read the requested table entry 
    ; into a temporary buffer
    ; ecx holds the entry number
    
    mov     eax, ecx
    mov     ecx, 14 ; ARP_ENTRY_SIZE
    mul     ecx
    
    mov     ecx, [eax + ARPTable]
    mov     [ARPTmp], ecx
    mov     ecx, [eax + ARPTable+4]
    mov     [ARPTmp+4], ecx
    mov     ecx, [eax + ARPTable+8]
    mov     [ARPTmp+8], ecx
    mov     cx, [eax + ARPTable+12]
    mov     [ARPTmp+12], cx
    ret
        
notsis202:    
    cmp     ebx, 203
    jnz     notsis203

    ; 203 - return IP address
    mov     eax, [ARPTmp]
    ret
    
notsis203:
    cmp     ebx, 204
    jnz     notsis204

    ; 204 - return MAC high dword
    mov     eax, [ARPTmp+4]
    ret
    
notsis204:
    cmp     ebx, 205
    jnz     notsis205

    ; 205 - return MAC ls word
    movzx   eax, word [ARPTmp+8]
    ret
    
notsis205:
    cmp     ebx, 206
    jnz     notsis206

    ; 206 - return status word
    movzx   eax, word [ARPTmp+10]
    ret
    
notsis206:
    cmp     ebx, 207
    jnz     notsis207

    ; 207 - return ttl word
    movzx   eax, word [ARPTmp+12]
    ret
    
notsis207:
    cmp     ebx, 2
    jnz     notsis2
   
    ;  2 : return number of IP packets received
    mov     eax, [ip_rx_count]
    ret
   
notsis2:
    cmp     ebx, 3
    jnz     notsis3
   
    ;  3 : return number of packets transmitted
    mov     eax, [ip_tx_count]
    ret
   
notsis3:
    cmp     ebx, 4
    jnz     notsis4
   
    ;  4 : return number of received packets dumped
    mov     eax, [dumped_rx_count]
    ret
   
notsis4:
    cmp     ebx, 5
    jnz     notsis5
   
    ;  5 : return number of arp packets received
    mov     eax, [arp_rx_count]
    ret
   
notsis5:
    cmp     ebx, 6
    jnz     notsis6
   
    ;  6 : return status of packet driver
    ;  ( 0 == not active, FFFFFFFF = successful )
    mov     eax, [eth_status]
    ret
   
notsis6:
    xor     eax, eax
    ret



;***************************************************************************
;   Function
;      stack_get_packet
;
;   Description
;       extracts an IP packet from the NET1 output queue
;       and sends the data to the calling process
;       pointer to data in edx
;       returns number of bytes read in eax
;
;***************************************************************************
stack_get_packet:
    ; Look for a buffer to tx
    mov     eax, NET1OUT_QUEUE
    call    dequeue
    cmp     ax, NO_BUFFER
    je      sgp_non_exit            ; Exit if no buffer available

    push    eax                     ; Save buffer number for freeing at end
    
    push    edx
    ; convert buffer pointer eax to the absolute address
    mov     ecx, IPBUFFSIZE
    mul     ecx
    add     eax, IPbuffs
    pop     edx
    
    push    eax                     ; save address of IP data
    
    ; Get the address of the callers data
    mov     edi,[0x3010]
    add     edi,0x10
    add     edx,[edi]
    mov     edi, edx
   
    pop     eax
    
    mov     ecx, 1500           ; should get the actual number of bytes to write
    mov     esi, eax
    cld
    rep     movsb               ; copy the data across

    ; And finally, return the buffer to the free queue
    pop     eax
    call    freeBuff

    mov     eax, 1500
    ret

sgp_non_exit:
    xor     eax, eax
    ret



;***************************************************************************
;   Function
;      stack_insert_packet
;
;   Description
;       writes an IP packet into the stacks receive queue
;       # of bytes to write in ecx
;       pointer to data in edx
;       returns 0 in eax ok, -1 == failed 
;
;***************************************************************************
stack_insert_packet:

    mov     eax, EMPTY_QUEUE
    call    dequeue
    cmp     ax, NO_BUFFER
    je      sip_err_exit
    
    push    eax
    
    ; save the pointers to the data buffer & size
    push    edx
    push    ecx

    ; convert buffer pointer eax to the absolute address
    mov     ecx, IPBUFFSIZE
    mul     ecx
    add     eax, IPbuffs

    mov     edx, eax
   
    ; So, edx holds the IPbuffer ptr

    pop     ecx                     ; count of bytes to send
    mov     ebx, ecx                ; need the length later
    pop     eax                     ; get callers ptr to data to send
   
    ; Get the address of the callers data
    mov     edi,[0x3010]
    add     edi,0x10
    add     eax,[edi]
    mov     esi, eax
   
    mov     edi, edx
    cld
    rep     movsb               ; copy the data across

    pop     ebx

    mov     eax, IPIN_QUEUE
    call    queue

    inc     dword [ip_rx_count]

    mov     eax, 0
    ret
    
sip_err_exit:
    mov     eax, 0xFFFFFFFF
    ret

   
   
;***************************************************************************
;   Function
;      socket_open
;
;   Description
;       find a free socket
;       local port in ebx
;       remote port in ecx
;       remote ip in edx
;       return socket # in eax, -1 if none available
;
;***************************************************************************
socket_open:
    call    get_free_socket
   
    cmp     eax, 0xFFFFFFFF
    jz      so_exit
   
    ; ax holds the socket number that is free. Get real address
    push    eax
    shl     eax, 12
    add     eax, sockets
   
    mov     [eax], dword SOCK_OPEN
   
    mov     [eax + 12], byte bh      ; Local port ( LS 16 bits )
    mov     [eax + 13], byte bl      ; Local port ( LS 16 bits )
    mov     ebx, [stack_ip]
    mov     [eax + 8], ebx         ; Local IP
    mov     [eax + 20], ch         ; Remote Port ( LS 16 bits )
    mov     [eax + 21], cl         ; Remote Port ( LS 16 bits )
    mov     [eax + 16], edx         ; Remote IP ( in Internet order )
    mov     [eax + 24], dword 0      ; recieved data count
   
    mov     esi, [0x3010]
    mov     ebx, [esi+0x4]
    mov     [eax + 4], ebx         ; save the process ID
    pop     eax      ; Get the socket number back, so we can return it
   
so_exit:
    ret
   


;***************************************************************************
;   Function
;      socket_open_tcp
;
;   Description
;       Opens a TCP socket in PASSIVE or ACTIVE mode
;       find a free socket
;       local port in ebx ( intel format )
;       remote port in ecx ( intel format )
;       remote ip in edx ( in Internet byte order )
;       Socket open mode in esi  ( SOCKET_PASSIVE or SOCKET_ACTIVE )
;       return socket # in eax, -1 if none available
;
;***************************************************************************
socket_open_tcp:
    call    get_free_socket
   
    cmp     eax, 0xFFFFFFFF
    jz      so_exit
   
    ; ax holds the socket number that is free. Get real address
    push    eax
    shl     eax, 12
    add     eax, sockets

    mov     [sktAddr], eax
    mov     [eax], dword SOCK_OPEN
    
    ; TODO - check this works!
    mov     [eax + 72], dword 0     ; Reset the window timer.
   
    mov     [eax + 12], byte bh      ; Local port ( LS 16 bits )
    mov     [eax + 13], byte bl      ; Local port ( LS 16 bits )
    mov     ebx, [stack_ip]
    mov     [eax + 8], ebx         ; Local IP
    mov     [eax + 20], ch         ; Remote Port ( LS 16 bits )
    mov     [eax + 21], cl         ; Remote Port ( LS 16 bits )
    mov     [eax + 16], edx         ; Remote IP ( in Internet order )
    mov     [eax + 24], dword 0      ; recieved data count
   
    ; Now fill in TCB state
    mov     ebx, TCB_LISTEN
    cmp     esi, SOCKET_PASSIVE
    jz      sot_001
    mov     ebx, TCB_SYN_SENT

sot_001:
    mov     [eax + 28], ebx            ; Indicate the state of the TCB

    mov     esi, [0x3010]
    mov     ecx, [esi+0x4]
    mov     [eax + 4], ecx         ; save the process ID
      
    cmp     ebx, TCB_LISTEN
    je      sot_done    
    
    ; Now, if we are in active mode, then we have to send a SYN to the specified remote port
   
  
    mov     eax, EMPTY_QUEUE
    call    dequeue
    cmp     ax, NO_BUFFER
    je      sot_done
   
    push    eax
    
    mov     bl, 0x02        ; SYN
    mov     ecx, 0
    
    call    buildTCPPacket    
    
    mov     eax, NET1OUT_QUEUE

    mov     edx, [stack_ip]
    mov     ecx, [ sktAddr ]
    mov     ecx, [ ecx + 16 ]
    cmp     edx, ecx
    jne     sot_notlocal
    mov     eax, IPIN_QUEUE

sot_notlocal:
       ; Send it.
    pop     ebx
    call    queue

    mov     esi, [sktAddr]
    
    ; increment SND.NXT in socket
    add     esi, 48
    call    inc_inet_esi
     
sot_done:
    pop     eax      ; Get the socket number back, so we can return it
   
sot_exit:
    ret

   
   
;***************************************************************************
;   Function
;      socket_close
;
;   Description
;       socket # in ebx
;       returns 0 for ok, -1 for socket not open (fail)
;
;***************************************************************************
socket_close:
    shl     ebx, 12
    add     ebx, sockets
    mov     eax, 0xFFFFFFFF         ; assume this operation will fail..
    cmp     [ebx], dword SOCK_EMPTY
    jz      sc_exit
   
    ; Clear the socket varaibles
    xor     eax, eax
    mov     edi,ebx
    mov     ecx,SOCKETHEADERSIZE
    cld
    rep     stosb
   
sc_exit:
    ret
    
   

;***************************************************************************
;   Function
;      socket_close_tcp
;
;   Description
;       socket # in ebx
;       returns 0 for ok, -1 for socket not open (fail)
;
;***************************************************************************
socket_close_tcp:
    ; first, remove any resend entries
    pusha

    mov     esi, resendQ
    mov     ecx, 0
    
sct001:
    cmp     ecx, NUMRESENDENTRIES
    je      sct003              ; None left
    cmp     [esi], bl
    je      sct002              ; found one
    inc     ecx
    add     esi, 4
    jmp     sct001
    
sct002:
    dec     dword [arp_rx_count] ; ************ TEST ONLY!
    
    mov     [esi], byte 0xFF
    jmp     sct001

sct003:
    popa
    
    shl     ebx, 12
    add     ebx, sockets
    mov     [sktAddr], ebx
    mov     eax, 0xFFFFFFFF         ; assume this operation will fail..
    cmp     [ebx], dword SOCK_EMPTY
    jz      sct_exit

    ; Now construct the response, and queue for sending by IP
    mov     eax, EMPTY_QUEUE
    call    dequeue
    cmp     ax, NO_BUFFER
    je      stl_exit

    push    eax
    
    mov     bl, 0x11        ; FIN + ACK
    mov     ecx, 0
    mov     esi, 0
    
    call    buildTCPPacket
    
    mov     ebx, [sktAddr]   

    ; increament SND.NXT in socket
    mov     esi, 48
    add     esi, ebx
    call    inc_inet_esi


    ; Get the socket state
    mov     eax, [ebx + 28]
    cmp     eax, TCB_LISTEN
    je      destroyTCB
    cmp     eax, TCB_SYN_SENT
    je      destroyTCB
    cmp     eax, TCB_SYN_RECEIVED
    je      sct_finwait1
    cmp     eax, TCB_ESTABLISHED
    je      sct_finwait1
    
    ; assume CLOSE WAIT
    ; Send a fin, then enter last-ack state
    mov     eax, TCB_LAST_ACK
    mov     [ebx + 28], eax
    xor     eax, eax
    jmp     sct_send
       
sct_finwait1:
    ; Send a fin, then enter finwait2 state
    mov     eax, TCB_FIN_WAIT_1
    mov     [ebx + 28], eax
    xor     eax, eax

sct_send:
    mov     eax, NET1OUT_QUEUE

    mov     edx, [stack_ip]
    mov     ecx, [ sktAddr ]
    mov     ecx, [ ecx + 16 ]
    cmp     edx, ecx
    jne     sct_notlocal
    mov     eax, IPIN_QUEUE

sct_notlocal:
       ; Send it.
    pop     ebx
    call    queue
    jmp     sct_exit
      
destroyTCB:
    pop     eax
    ; Clear the socket varaibles
    xor     eax, eax
    mov     edi,ebx
    mov     ecx,SOCKETHEADERSIZE
    cld
    rep     stosb
   
sct_exit:
    ret


   
;***************************************************************************
;   Function
;      socket_poll
;
;   Description
;       socket # in ebx
;       returns count in eax.
;
;***************************************************************************
socket_poll:
    shl     ebx, 12
    add     ebx, sockets
    mov     eax, [ebx + 24]
    
    ret
   
   
   
;***************************************************************************
;   Function
;      socket_status
;
;   Description
;       socket # in ebx
;       returns TCB state in eax.
;
;***************************************************************************
socket_status:
    shl     ebx, 12
    add     ebx, sockets
    mov     eax, [ebx + 28]
    
    ret
   
   
   
;***************************************************************************
;   Function
;      socket_read
;
;   Description
;       socket # in ebx
;       returns # of bytes remaining in eax, data in bl
;
;***************************************************************************
socket_read:
    shl     ebx, 12
    add     ebx, sockets
    mov     eax, [ebx + 24]         ; get count of bytes
    mov     ecx,1
    test    eax, eax
    jz      sr2
   
    dec     eax
    mov     esi, ebx            ; esi is address of socket
    mov     [ebx + 24], eax         ; store new count
    movzx   ebx, byte [ebx + SOCKETHEADERSIZE]  ; get the byte
    add     esi, SOCKETHEADERSIZE
    mov     edi, esi
    inc     esi
   
    mov     ecx, (SOCKETBUFFSIZE - SOCKETHEADERSIZE) / 4
    cld
    rep     movsd
    xor     ecx, ecx
    
sr1:
    jmp     sor_exit
   
sr2:
    xor     bl, bl
   
sor_exit:
    ret
   
   
   
;***************************************************************************
;   Function
;      socket_write
;
;   Description
;       socket in ebx
;       # of bytes to write in ecx
;       pointer to data in edx
;       returns 0 in eax ok, -1 == failed ( invalid socket, or
;       could not queue IP packet )
;
;***************************************************************************
socket_write:
    ; First, find the address of the socket descriptor
    shl     ebx, 12
    add     ebx, sockets         ; ebx = address of actual socket
   
    mov     eax, 0xFFFFFFFF
    ; If the socket is invalid, return with an error code
    cmp     [ebx], dword SOCK_EMPTY
    je      sw_exit


    mov     eax, EMPTY_QUEUE
    call    dequeue
    cmp     ax, NO_BUFFER
    je      sw_exit
   
    ; Save the queue entry number
    push    eax
   
    ; save the pointers to the data buffer & size
    push    edx
    push    ecx

    ; convert buffer pointer eax to the absolute address
    mov     ecx, IPBUFFSIZE
    mul     ecx
    add     eax, IPbuffs

    mov     edx, eax
   
    ; So, ebx holds the socket ptr, edx holds the IPbuffer ptr
   
    ; Fill in the IP header ( some data is in the socket descriptor)
    mov     eax, [ebx + 8]
    mov     [edx + 12], eax      ; source IP
    mov     eax, [ebx + 16]
    mov     [edx + 16], eax      ; Destination IP
   
    mov     al, 0x45
    mov     [edx], al         ; Version, IHL
    xor     al, al   
    mov     [edx + 1], al     ; Type of service
   
    pop     eax                   ; Get the UDP data length
    push    eax
   
    add     eax, 20 + 8           ; add IP header and UDP header lengths
    mov     [edx + 2], ah
    mov     [edx + 3], al
    xor     al, al   
    mov     [edx + 4], al
    mov     [edx + 5], al
    mov     al, 0x40
    mov     [edx + 6], al
    xor     al, al  
    mov     [edx + 7], al
    mov     al, 0x20
    mov     [edx + 8], al
    mov     al, 17
    mov     [edx + 9], al
   
    ; Checksum left unfilled
    xor     ax, ax
    mov     [edx + 10], ax
   
    ; Fill in the UDP header ( some data is in the socket descriptor)
    mov     ax, [ebx + 12]
    mov     [edx + 20], ax
   
    mov     ax, [ebx + 20]
    mov     [edx + 20 + 2], ax
   
    pop     eax
    push    eax
   
    add     eax, 8
    mov     [edx + 20 + 4], ah
    mov     [edx + 20 + 5], al
   
    ; Checksum left unfilled
    xor     ax, ax
    mov     [edx + 20 + 6], ax
   
    pop     ecx                  ; count of bytes to send
    mov     ebx, ecx            ; need the length later
    pop     eax                  ; get callers ptr to data to send
   
    ; Get the address of the callers data
    mov     edi,[0x3010]
    add     edi,0x10
    add     eax,[edi]
    mov     esi, eax
   
    mov     edi, edx
    add     edi, 28
    cld
    rep     movsb               ; copy the data across
   
    ; we have edx as IPbuffer ptr.
    ; Fill in the UDP checksum
    ; First, fill in pseudoheader
    mov     eax, [edx + 12]
    mov     [pseudoHeader], eax
    mov     eax, [edx + 16]
    mov     [pseudoHeader+4], eax
    mov     ax, 0x1100            ; 0 + protocol
    mov     [pseudoHeader+8], ax
    add     ebx, 8
    mov     eax, ebx
    mov     [pseudoHeader+10], ah
    mov     [pseudoHeader+11], al
      
    mov     eax, pseudoHeader
    mov     [checkAdd1], eax
    mov     [checkSize1], word 12
    mov     eax, edx
    add     eax, 20
    mov     [checkAdd2], eax
    mov     eax, ebx
    mov     [checkSize2], ax      ; was eax!! mjh 8/7/02
   
    call    checksum
   
    ; store it in the UDP checksum ( in the correct order! )
    mov     ax, [checkResult]

    ; If the UDP checksum computes to 0, we must make it 0xffff
    ; (0 is reserved for 'not used')
    cmp     ax, 0
    jne     sw_001
    mov     ax, 0xffff
   
sw_001:
    mov     [edx + 20 + 6], ah
    mov     [edx + 20 + 7], al
   
    ; Fill in the IP header checksum
    mov     eax, edx
    mov     [checkAdd1], eax
    mov     [checkSize1], word 20
    mov     [checkAdd2], dword 0
    mov     [checkSize2], word 0
   
    call    checksum
   
    mov     ax, [checkResult]
    mov     [edx + 10], ah
    mov     [edx + 11], al
   
    ; Check destination IP address.
    ; If it is the local host IP, route it back to IP_RX
   
    pop     ebx   
    mov     eax, NET1OUT_QUEUE

    mov     ecx, [ edx + 16]
    mov     edx, [stack_ip]
    cmp     edx, ecx
    jne     sw_notlocal
    mov     eax, IPIN_QUEUE

sw_notlocal:
    ; Send it.
    call    queue
    
    xor     eax, eax

sw_exit:
    ret
   


;***************************************************************************
;   Function
;      socket_write_tcp
;
;   Description
;       socket in ebx
;       # of bytes to write in ecx
;       pointer to data in edx
;       returns 0 in eax ok, -1 == failed ( invalid socket, or
;       could not queue IP packet )
;
;***************************************************************************
socket_write_tcp:
    ; First, find the address of the socket descriptor
    shl     ebx, 12
    add     ebx, sockets         ; ebx = address of actual socket

    mov     [sktAddr], ebx
   
    mov     eax, 0xFFFFFFFF
    ; If the socket is invalid, return with an error code
    cmp     [ebx], dword SOCK_EMPTY
    je      swt_exit

    ; If the sockets window timer is nonzero, do not queue packet
    ; TODO - done
    cmp     [ebx + 72], dword 0
    jne     swt_exit

    mov     eax, EMPTY_QUEUE
    call    dequeue
    cmp     ax, NO_BUFFER
    je      swt_exit
   
    push    eax
    
    mov     bl, 0x10        ; ACK

    ; Get the address of the callers data
    mov     edi,[0x3010]
    add     edi,0x10
    add     edx,[edi]
    mov     esi, edx
    
    pop     eax
    push    eax
    
    push    ecx
    call    buildTCPPacket
    pop     ecx
    
    ; Check destination IP address.
    ; If it is the local host IP, route it back to IP_RX
    
    pop     ebx   
    push    ecx 
    mov     eax, NET1OUT_QUEUE

    mov     edx, [stack_ip]
    mov     ecx, [ sktAddr ]
    mov     ecx, [ ecx + 16 ]
    cmp     edx, ecx
    jne     swt_notlocal
    mov     eax, IPIN_QUEUE
    
swt_notlocal:
    pop     ecx 

    push    ebx                 ; save ipbuffer number
    
    call    queue

    mov     esi, [sktAddr]
    
    ; increament SND.NXT in socket
    ; Amount to increment by is in ecx
    add     esi, 48
    call    add_inet_esi

    pop     ebx
       
    ; Copy the IP buffer to a resend queue
    ; If there isn't one, dont worry about it for now
    mov     esi, resendQ
    mov     ecx, 0
    
swt003:
    cmp     ecx, NUMRESENDENTRIES
    je      swt001              ; None found
    cmp     [esi], byte 0xFF
    je      swt002              ; found one
    inc     ecx
    add     esi, 4
    jmp     swt003
    
swt002:
    push    ebx
    
    ; OK, we have a buffer descriptor ptr in esi. 
    ; resend entry # in ecx
    ;  Populate it
    ;  socket #
    ;  retries count
    ;  retry time
    ;  fill IP buffer associated with this descriptor    

    mov     eax, [sktAddr]
    sub     eax, sockets
    shr     eax, 12             ; get skt #
    mov     [esi], al
    mov     [esi + 1], byte TCP_RETRIES
    mov     [esi + 2], word TCP_TIMEOUT
    
    inc     ecx
    ; Now get buffer location, and copy buffer across. argh! more copying,,
    mov     edi, resendBuffer - IPBUFFSIZE
swt002a:
    add     edi, IPBUFFSIZE
    loop    swt002a
    
    ; we have dest buffer location in edi
    pop     eax
    ; convert source buffer pointer eax to the absolute address
    mov     ecx, IPBUFFSIZE
    mul     ecx
    add     eax, IPbuffs
    mov     esi, eax
    
    ; do copy
    mov     ecx, IPBUFFSIZE
    cld
    rep     movsb
    
    inc     dword [arp_rx_count] ; ************ TEST ONLY!
    
swt001:    
    xor     eax, eax
    
swt_exit:
    ret


   
; Below, the main network layer source code is included
; 
   
include "QUEUE.INC"
include "IP.INC"
include "TCP.INC"   
include "UDP.INC"   
include "ETHERNET.INC"
